/*
 * Copyright (c) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "timeline_napi.h"

#include "timeline_common.h"
#include "timeline_client.h"
#include "timeline_database_info.h"

using namespace OHOS::Msdp;
using namespace OHOS;
namespace {
auto &g_TimelineClient = TimelineClient::GetInstance();
									  
static const uint8_t ARG_0 = 0;
static const uint8_t ARG_1 = 1;
static const uint8_t ARG_2 = 2;
static const int32_t CALLBACK_SUCCESS = 200;
static const int32_t ERROR_MESSAGE = -1;

static const std::vector<std::string> vecTimelineValue {
    "VALUE_HOME", "VALUE_COMPANY", "VALUE_UNKNOWN"
};
}
std::map<int32_t, sptr<ITimelineCallback>> TimelineNapi::callbackMap_;
std::map<int32_t, TimelineNapi*> TimelineNapi::objectMap_;
napi_ref TimelineNapi::timelineValueRef_;

struct ResponseEntity {
    TimelineDataUtils::TimelineValue value;
};


void TimelineCallback::OnTimelineChanged(const TimelineDataUtils::TimelineData& timelineData)
{
    TIMELINE_HILOGD(TIMELINE_MODULE_JS_NAPI, "Callback enter");
    TimelineNapi *TimelineNapi = TimelineNapi::GetTimelineNapi(timelineData.area);
    if (TimelineNapi == nullptr) {
        return;
    }
    TimelineNapi->OnTimelineChangedDone(static_cast<int32_t> (timelineData.area),
        static_cast<int32_t> (timelineData.value));
    TIMELINE_HILOGD(TIMELINE_MODULE_JS_NAPI, "Callback exit");
}

TimelineNapi *TimelineNapi::GetTimelineNapi(int32_t area)
{
    TIMELINE_HILOGD(TIMELINE_MODULE_JS_NAPI, "Enter, area = %{public}d", area);
    TimelineNapi* obj = nullptr;
    bool isExists = false;
    for (auto it = objectMap_.begin(); it != objectMap_.end(); ++it) {
        if (it->first == area) {
            isExists = true;
            obj = (TimelineNapi*)(it->second);
            TIMELINE_HILOGD(TIMELINE_MODULE_JS_NAPI, "Found object");
            break;
        }
    }
    if (!isExists) {
        TIMELINE_HILOGE(TIMELINE_MODULE_JS_NAPI, "Didn't find object");
    }	
    return obj;
}

void TimelineNapi::InvokeCallBack(napi_env env, napi_value *args, bool voidParameter, int32_t value)
{
    napi_value callback = nullptr;
    napi_value indexObj = nullptr;
    napi_create_object(env, &indexObj);
    napi_value successIndex = nullptr;
    if (!voidParameter){
        napi_create_int32(env, value, &successIndex);
    }
    
    napi_ref callbackSuccess = nullptr;
    napi_value ret;
    napi_set_named_property(env, indexObj, "TimelineValue", successIndex);
    napi_create_reference(env, args[ARG_1], 1, &callbackSuccess);
    napi_get_reference_value(env, callbackSuccess, &callback);
    napi_call_function(env, nullptr, callback, 1, &indexObj, &ret);
    napi_delete_reference(env, callbackSuccess);
}

TimelineNapi::TimelineNapi(napi_env env, napi_value thisVar) : TimelineEvent(env, thisVar)
{
    env_ = env;
    callbackRef_ = nullptr;
	timelineValueRef_ = nullptr;
}

TimelineNapi::~TimelineNapi()
{
    if (callbackRef_ != nullptr) {
        napi_delete_reference(env_, callbackRef_);
    }

	if (timelineValueRef_ != nullptr) {
        napi_delete_reference(env_, timelineValueRef_);
    }
}

void TimelineNapi::OnTimelineChangedDone(const int32_t& type, const int32_t& value)
{
    TIMELINE_HILOGD(TIMELINE_MODULE_JS_NAPI, "Enter, value = %{public}d", value);
    napi_value timelineValue;
    NAPI_CALL_RETURN_VOID(env_, napi_create_int32(env_, value, &timelineValue));
    once(type, ARG_1, &timelineValue);
    TIMELINE_HILOGD(TIMELINE_MODULE_JS_NAPI, "Exit");
}

napi_value TimelineNapi::SubscribeTimeline(napi_env env, napi_callback_info info)
{
    TIMELINE_HILOGD(TIMELINE_MODULE_JS_NAPI, "Enter");
    napi_value result = nullptr;
    size_t argc = ARG_2;
    napi_value args[ARG_2] = {0};
    napi_value jsthis;
    void *data = nullptr;
    TimelineNapi *obj = nullptr;
    sptr<ITimelineCallback> callback_ = nullptr;

    napi_status status = napi_get_cb_info(env, info, &argc, args, &jsthis, &data);
    NAPI_ASSERT(env, (status == napi_ok) && (argc >= 0), "Bad parameters");

    napi_valuetype valueType1 = napi_undefined;
    napi_typeof(env, args[ARG_0], &valueType1);
    NAPI_ASSERT(env, valueType1 == napi_number, "type mismatch for parameter 1");

    napi_valuetype valueType2 = napi_undefined;
    napi_typeof(env, args[ARG_1], &valueType2);
    NAPI_ASSERT(env, valueType2 == napi_function, "type mismatch for parameter 2");

    int32_t area;
    status = napi_get_value_int32(env, args[ARG_0], &area);
    if (status != napi_ok) {
        TIMELINE_HILOGE(TIMELINE_MODULE_JS_NAPI, "Failed to get area");
        return result;
    }
    if (area != TimelineDataUtils::TimelineArea::AREA_COMPANY && area != TimelineDataUtils::TimelineArea::AREA_HOME &&
        area != TimelineDataUtils::TimelineArea::AREA_UNKNOWN) {
            InvokeCallBack(env, args, false, ERROR_MESSAGE);
            return result;
    }

    bool isObjExists = false;
    for (auto it = objectMap_.begin(); it != objectMap_.end(); ++it) {
        if (it->first == area) {
            isObjExists = true;
            TIMELINE_HILOGE(TIMELINE_MODULE_JS_NAPI, "Object already exists");
            return result;
        }
    }

    if (!isObjExists) {
        TIMELINE_HILOGE(TIMELINE_MODULE_JS_NAPI, "Didn't find object");
        obj = new TimelineNapi(env, jsthis);
        napi_wrap(env, jsthis, reinterpret_cast<void *>(obj),
            [](napi_env env, void *data, void *hint) {
                (void)env;
                (void)hint;
                TimelineNapi *timeline = (TimelineNapi *)data;
                delete timeline;
            },
            nullptr, &(obj->callbackRef_));
        objectMap_.insert(std::pair<int32_t, TimelineNapi*>(area, obj));
    }
    obj->on(area, args[ARG_1]);
       
    // obj->on(area, args[ARG_1]);
    
    // sptr<IMovementCallback> callback;
    bool isCallbackExists = false;
    for (auto it = callbackMap_.begin(); it != callbackMap_.end(); ++it) {
        if (it->first == area) {
            isCallbackExists = true;
            break;
        }
    }
    if (!isCallbackExists) {
        callback_ = new TimelineCallback();
        TIMELINE_HILOGE(TIMELINE_MODULE_JS_NAPI, "Didn't find callback, created it: %{public}p", callback_.GetRefPtr());
        g_TimelineClient.Subscribe(TimelineDataUtils::TimelineArea(area), callback_);
        callbackMap_.insert(std::pair<int32_t, sptr<ITimelineCallback>>(area, callback_));
        InvokeCallBack(env, args, false, CALLBACK_SUCCESS);
    } else {
        TIMELINE_HILOGE(TIMELINE_MODULE_JS_NAPI, "Callback exists.");
        g_TimelineClient.Subscribe(TimelineDataUtils::TimelineArea(area), callback_);
        return result;
    }

    // if (callback_ != nullptr) {
    //     g_TimelineClient.Subscribe(TimelineDataUtils::TimelineArea(area), callback_);
    // }
    napi_get_undefined(env, &result);
    return result;
    TIMELINE_HILOGD(TIMELINE_MODULE_JS_NAPI, "Exit");	  
}

napi_value TimelineNapi::UnSubscribeTimeline(napi_env env, napi_callback_info info)
{
    TIMELINE_HILOGD(TIMELINE_MODULE_JS_NAPI, "Enter");
    napi_value result = nullptr;
    size_t argc = ARG_2;
    napi_value args[ARG_2] = { 0 };
    napi_value jsthis;
    void *data = nullptr;

    napi_status status = napi_get_cb_info(env, info, &argc, args, &jsthis, &data);
    NAPI_ASSERT(env, (status == napi_ok) && (argc >= 0), "Bad parameters");

    napi_valuetype valueType1 = napi_undefined;
    napi_typeof(env, args[ARG_0], &valueType1);
    TIMELINE_HILOGD(TIMELINE_MODULE_JS_NAPI, "valueType1: %{public}d", valueType1);
    NAPI_ASSERT(env, valueType1 == napi_number, "type mismatch for parameter 1");

    int32_t area;
    status = napi_get_value_int32(env, args[ARG_0], &area);
    if (status != napi_ok) {
        TIMELINE_HILOGE(TIMELINE_MODULE_JS_NAPI, "Failed to get type");
        return result;
    }
    if (area != TimelineDataUtils::TimelineArea::AREA_COMPANY && area != TimelineDataUtils::TimelineArea::AREA_HOME &&
        area != TimelineDataUtils::TimelineArea::AREA_UNKNOWN) {
            InvokeCallBack(env, args, false, ERROR_MESSAGE);
            return result;
    }

    TimelineNapi *obj = nullptr;
    bool isObjExists = false;
    for (auto it = objectMap_.begin(); it != objectMap_.end(); ++it) {
        if (it->first == area) {
            isObjExists = true;
            obj = (TimelineNapi*)(it->second);
            TIMELINE_HILOGD(TIMELINE_MODULE_JS_NAPI, "Found object");
        }
    }
    if (!isObjExists) {
        TIMELINE_HILOGE(TIMELINE_MODULE_JS_NAPI, "Didn't find object, so created it");
        return result;
    }    

    obj->off(area);
    objectMap_.erase(area);

    sptr<ITimelineCallback> callback_;
    bool isCallbackExists = false;
    for (auto it = callbackMap_.begin(); it != callbackMap_.end(); ++it) {
        if (it->first == area) {
            isCallbackExists = true;
            callback_ = (sptr<ITimelineCallback>)(it->second);
            TIMELINE_HILOGD(TIMELINE_MODULE_JS_NAPI, "Found callback: %{public}p", callback_.GetRefPtr());
            break;
        }
    }
    if (!isCallbackExists) {
        TIMELINE_HILOGE(TIMELINE_MODULE_JS_NAPI, "No existed callback");
        return result;
    } else if (callback_ != nullptr) {
        g_TimelineClient.UnSubscribe(TimelineDataUtils::TimelineArea(area), callback_);
        InvokeCallBack(env, args, true, CALLBACK_SUCCESS);
        callbackMap_.erase(area);
    }

    //if (callback_ != nullptr) {
    //    g_TimelineClient.UnSubscribe(TimelineDataUtils::TimelineArea(type), callback_);
   // }
    napi_get_undefined(env, &result);
    return result;
    TIMELINE_HILOGD(TIMELINE_MODULE_JS_NAPI, "Exit");
}

napi_value TimelineNapi::GetTimeline(napi_env env, napi_callback_info info)
{
    TIMELINE_HILOGD(TIMELINE_MODULE_JS_NAPI, "Enter");
    napi_value napiValue = nullptr;
    // TO-DO, notify the callback and unsubscribe it

    TIMELINE_HILOGD(TIMELINE_MODULE_JS_NAPI, "Exit");
    return napiValue;
}

napi_value TimelineNapi::EnumTimelineAreaConstructor(napi_env env, napi_callback_info info)
{
    TIMELINE_HILOGD(TIMELINE_MODULE_JS_NAPI, "Enter");
    napi_value thisArg = nullptr;
    void *data = nullptr;
    napi_get_cb_info(env, info, nullptr, nullptr, &thisArg, &data);
    napi_value global = nullptr;
    napi_get_global(env, &global);
    TIMELINE_HILOGD(TIMELINE_MODULE_JS_NAPI, "Exit");
    return thisArg;
}

napi_value TimelineNapi::CreateEnumTimelineArea(napi_env env, napi_value exports)
{
    TIMELINE_HILOGD(TIMELINE_MODULE_JS_NAPI, "Enter");
    napi_value home = nullptr;
    napi_value company = nullptr;
    napi_value unknown= nullptr;

    napi_create_int32(env, (int32_t)TimelineDataUtils::TimelineArea::AREA_HOME, &home);
    napi_create_int32(env, (int32_t)TimelineDataUtils::TimelineArea::AREA_COMPANY, &company);
    napi_create_int32(env, (int32_t)TimelineDataUtils::TimelineArea::AREA_UNKNOWN, &unknown);

    napi_property_descriptor desc[] = {
        DECLARE_NAPI_STATIC_PROPERTY("AREA_HOME", home),
        DECLARE_NAPI_STATIC_PROPERTY("AREA_COMPANY", company),
        DECLARE_NAPI_STATIC_PROPERTY("AREA_UNKNOWN", unknown),																		
    };
    napi_value result = nullptr;
    napi_define_class(env, "TimelineArea", NAPI_AUTO_LENGTH, EnumTimelineAreaConstructor, nullptr,
        sizeof(desc) / sizeof(*desc), desc, &result);

    napi_set_named_property(env, exports, "TimelineArea", result);
    TIMELINE_HILOGD(TIMELINE_MODULE_JS_NAPI, "Exit");
    return exports;
}

napi_value TimelineNapi::EnumTimelineValueConstructor(napi_env env, napi_callback_info info)
{
    TIMELINE_HILOGD(TIMELINE_MODULE_JS_NAPI, "Enter");
    napi_value thisArg = nullptr;
    void *data = nullptr;
    napi_get_cb_info(env, info, nullptr, nullptr, &thisArg, &data);
    napi_value global = nullptr;
    napi_get_global(env, &global);
    TIMELINE_HILOGD(TIMELINE_MODULE_JS_NAPI, "Exit");
    return thisArg;
}

napi_value TimelineNapi::CreateTimelineValueType(napi_env env, napi_value exports)
{
    TIMELINE_HILOGD(TIMELINE_MODULE_JS_NAPI, "Enter");
    napi_value home = nullptr;
    napi_value company = nullptr;
	napi_value unknown = nullptr;

    napi_create_int32(env, (int32_t)TimelineDataUtils::TimelineValue::VALUE_HOME, &home);
    napi_create_int32(env, (int32_t)TimelineDataUtils::TimelineValue::VALUE_COMPANY, &company);
	napi_create_int32(env, (int32_t)TimelineDataUtils::TimelineValue::VALUE_UNKNOWN, &unknown);

    napi_property_descriptor desc[] = {
        DECLARE_NAPI_STATIC_PROPERTY("VALUE_HOME", home),
        DECLARE_NAPI_STATIC_PROPERTY("VALUE_COMPANY", company),
		DECLARE_NAPI_STATIC_PROPERTY("VALUE_UNKNOWN", unknown),
    };
    napi_value result = nullptr;
    napi_define_class(env, "TimelineValue", NAPI_AUTO_LENGTH, EnumTimelineValueConstructor, nullptr,
        sizeof(desc) / sizeof(*desc), desc, &result);

    napi_set_named_property(env, exports, "TimelineValue", result);
    TIMELINE_HILOGD(TIMELINE_MODULE_JS_NAPI, "Exit");
    return exports;
}

napi_status TimelineNapi::AddProperty(napi_env env, napi_value object, const std::string name, int32_t enumValue)
{
    TIMELINE_HILOGD(TIMELINE_MODULE_JS_NAPI, "Enter");
    napi_status status;
    napi_value enumNapiValue;

    status = napi_create_int32(env, enumValue, &enumNapiValue);
    if (status == napi_ok) {
        status = napi_set_named_property(env, object, name.c_str(), enumNapiValue);
    }
    TIMELINE_HILOGD(TIMELINE_MODULE_JS_NAPI, "Exit");
    return status;
}

napi_value TimelineNapi::CreateTimelineValueObject(napi_env env)
{
    TIMELINE_HILOGD(TIMELINE_MODULE_JS_NAPI, "Enter");
    napi_value result = nullptr;
    napi_status status;
    std::string propName;

    status = napi_create_object(env, &result);
    if (status == napi_ok) {
        for (unsigned int i = TimelineDataUtils::TimelineValue::VALUE_HOME; i < vecTimelineValue.size(); i++) {
            propName = vecTimelineValue[i];
            TIMELINE_HILOGD(TIMELINE_MODULE_JS_NAPI, "propName: %{public}s", propName.c_str());
            status = AddProperty(env, result, propName, i);
            if (status != napi_ok) {
                TIMELINE_HILOGE(TIMELINE_MODULE_JS_NAPI, "Failed to add named prop!");
                break;
            }
            propName.clear();
        }
    }
    if (status == napi_ok) {
        // The reference count is for creation of timeline value Object Reference
        status = napi_create_reference(env, result, 1, &timelineValueRef_);
        if (status == napi_ok) {
            return result;
        }
    }
    TIMELINE_HILOGE(TIMELINE_MODULE_JS_NAPI, "CreateTimelineValueObject is Failed!");
    napi_get_undefined(env, &result);

    return result;
}

napi_value TimelineNapi::Init(napi_env env, napi_value exports)
{
    TIMELINE_HILOGD(TIMELINE_MODULE_JS_NAPI, "Enter");
    napi_property_descriptor desc[] = {
        DECLARE_NAPI_STATIC_FUNCTION("on", SubscribeTimeline),
        DECLARE_NAPI_STATIC_FUNCTION("off", UnSubscribeTimeline),
        DECLARE_NAPI_STATIC_FUNCTION("once", GetTimeline),
        DECLARE_NAPI_STATIC_FUNCTION("setPositionState", SetPositionState),
        DECLARE_NAPI_STATIC_FUNCTION("setHomePosition", SetHomePosition),
        DECLARE_NAPI_STATIC_FUNCTION("setCompanyPosition", SetCompanyPosition),
        DECLARE_NAPI_STATIC_FUNCTION("setSleepTime", SetSleepTime),
        DECLARE_NAPI_STATIC_FUNCTION("setRestTime", SetRestTime),
        DECLARE_NAPI_STATIC_FUNCTION("setWorkTime", SetWorkTime),
        DECLARE_NAPI_STATIC_FUNCTION("getForcecast", GetForcecast),
    };
    NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc));
    CreateEnumTimelineArea(env, exports);
    TIMELINE_HILOGD(TIMELINE_MODULE_JS_NAPI, "Exit");
    return exports;
}

struct PositionStateAsyncCallbackInfo {
    napi_env env;
    napi_async_work asyncWork;
    int32_t homevalue;
    int32_t companyvalue;
};

napi_value TimelineNapi::SetPositionState(napi_env env, napi_callback_info info)
{
    TIMELINE_HILOGD(TIMELINE_MODULE_JS_NAPI, "enter");
    size_t argc = ARG_2;
    napi_value args[ARG_2] = {0};
    napi_value jsthis;
    void *data = nullptr;

    napi_status status = napi_get_cb_info(env, info, &argc, args, &jsthis, &data);
    NAPI_ASSERT(env, (status == napi_ok) && (argc >= 2), "Failed to get cb info");

    napi_valuetype type;
    NAPI_CALL(env, napi_typeof(env, args[0], &type));

    NAPI_ASSERT(env, type == napi_number, "Wrong argument type. Numbers expected.");

    int32_t homevalue = 0;
    NAPI_CALL(env, napi_get_value_int32(env, args[ARG_0], &homevalue));

    NAPI_CALL(env, napi_typeof(env, args[1], &type));
    int32_t companyvalue = 0;
    NAPI_CALL(env, napi_get_value_int32(env, args[ARG_1], &companyvalue));

    PositionStateAsyncCallbackInfo* asyncCallbackInfo = new PositionStateAsyncCallbackInfo {
        .env = env,
        .asyncWork = nullptr,
    };

    asyncCallbackInfo->homevalue = homevalue;
    asyncCallbackInfo->companyvalue = companyvalue;

    napi_value resourceName;
    napi_create_string_latin1(env, "setPositionState", NAPI_AUTO_LENGTH, &resourceName);

    napi_create_async_work(
        env,
        nullptr,
        resourceName,
        [](napi_env env, void *data) {
            PositionStateAsyncCallbackInfo* asyncCallbackInfo = (PositionStateAsyncCallbackInfo *)data;
            if (!TimelineClient::GetInstance().SetPositionState(asyncCallbackInfo->homevalue, asyncCallbackInfo->companyvalue)) {
                TIMELINE_HILOGD(TIMELINE_MODULE_JS_NAPI, "Failed to set position state");
            } else {
                TIMELINE_HILOGD(TIMELINE_MODULE_JS_NAPI, "Succeed to set position state");
            }
        },
        [](napi_env env, napi_status status, void *data) {
            PositionStateAsyncCallbackInfo* asyncCallbackInfo = (PositionStateAsyncCallbackInfo *)data;
            napi_delete_async_work(env, asyncCallbackInfo->asyncWork);
            delete asyncCallbackInfo;
        },
        (void *)asyncCallbackInfo,
        &asyncCallbackInfo->asyncWork);

    NAPI_CALL(env, napi_queue_async_work(env, asyncCallbackInfo->asyncWork));

    TIMELINE_HILOGD(TIMELINE_MODULE_JS_NAPI, "return");
    return nullptr;
}

struct HomePositionAsyncCallbackInfo {
    napi_env env;
    napi_async_work asyncWork;
    double longitude;
    double latitude;
};

napi_value TimelineNapi::SetHomePosition(napi_env env, napi_callback_info info)
{
    TIMELINE_HILOGD(TIMELINE_MODULE_JS_NAPI, "enter");
    size_t argc = ARG_2;
    napi_value args[ARG_2] = {0};
    napi_value jsthis;
    void *data = nullptr;

    napi_status status = napi_get_cb_info(env, info, &argc, args, &jsthis, &data);
    NAPI_ASSERT(env, (status == napi_ok) && (argc >= 2), "Failed to get cb info");

    napi_valuetype type;
    NAPI_CALL(env, napi_typeof(env, args[0], &type));

    NAPI_ASSERT(env, type == napi_number, "Wrong argument type. Numbers expected.");

    double longitude = 0;
    NAPI_CALL(env, napi_get_value_double(env, args[ARG_0], &longitude));

    NAPI_CALL(env, napi_typeof(env, args[1], &type));
    double latitude = 0;
    NAPI_CALL(env, napi_get_value_double(env, args[ARG_1], &latitude));

    HomePositionAsyncCallbackInfo* asyncCallbackInfo = new HomePositionAsyncCallbackInfo {
        .env = env,
        .asyncWork = nullptr,
    };

    asyncCallbackInfo->longitude = longitude;
    asyncCallbackInfo->latitude = latitude;

    napi_value resourceName;
    napi_create_string_latin1(env, "setHomePosition", NAPI_AUTO_LENGTH, &resourceName);

    napi_create_async_work(
        env,
        nullptr,
        resourceName,
        [](napi_env env, void *data) {
            HomePositionAsyncCallbackInfo* asyncCallbackInfo = (HomePositionAsyncCallbackInfo *)data;
            if (!TimelineClient::GetInstance().SetHomePosition(asyncCallbackInfo->longitude, asyncCallbackInfo->latitude)) {
                TIMELINE_HILOGD(TIMELINE_MODULE_JS_NAPI, "Failed to set position state");
            } else {
                TIMELINE_HILOGD(TIMELINE_MODULE_JS_NAPI, "Succeed to set position state");
            }
        },
        [](napi_env env, napi_status status, void *data) {
            HomePositionAsyncCallbackInfo* asyncCallbackInfo = (HomePositionAsyncCallbackInfo *)data;
            napi_delete_async_work(env, asyncCallbackInfo->asyncWork);
            delete asyncCallbackInfo;
        },
        (void *)asyncCallbackInfo,
        &asyncCallbackInfo->asyncWork);

    NAPI_CALL(env, napi_queue_async_work(env, asyncCallbackInfo->asyncWork));

    TIMELINE_HILOGD(TIMELINE_MODULE_JS_NAPI, "return");
    return nullptr;
}

struct CompanyPositionAsyncCallbackInfo {
    napi_env env;
    napi_async_work asyncWork;
    double longitude;
    double latitude;
};

napi_value TimelineNapi::SetCompanyPosition(napi_env env, napi_callback_info info)
{
    TIMELINE_HILOGD(TIMELINE_MODULE_JS_NAPI, "enter");
    size_t argc = ARG_2;
    napi_value args[ARG_2] = {0};
    napi_value jsthis;
    void *data = nullptr;

    napi_status status = napi_get_cb_info(env, info, &argc, args, &jsthis, &data);
    NAPI_ASSERT(env, (status == napi_ok) && (argc >= 2), "Failed to get cb info");

    napi_valuetype type;
    NAPI_CALL(env, napi_typeof(env, args[0], &type));

    NAPI_ASSERT(env, type == napi_number, "Wrong argument type. Numbers expected.");

    double longitude = 0;
    NAPI_CALL(env, napi_get_value_double(env, args[ARG_0], &longitude));

    NAPI_CALL(env, napi_typeof(env, args[1], &type));
    double latitude = 0;
    NAPI_CALL(env, napi_get_value_double(env, args[ARG_1], &latitude));

    CompanyPositionAsyncCallbackInfo* asyncCallbackInfo = new CompanyPositionAsyncCallbackInfo {
        .env = env,
        .asyncWork = nullptr,
    };

    asyncCallbackInfo->longitude = longitude;
    asyncCallbackInfo->latitude = latitude;

    napi_value resourceName;
    napi_create_string_latin1(env, "setCompanyPosition", NAPI_AUTO_LENGTH, &resourceName);

    napi_create_async_work(
        env,
        nullptr,
        resourceName,
        [](napi_env env, void *data) {
            CompanyPositionAsyncCallbackInfo* asyncCallbackInfo = (CompanyPositionAsyncCallbackInfo *)data;
            if (!TimelineClient::GetInstance().SetCompanyPosition(asyncCallbackInfo->longitude, asyncCallbackInfo->latitude)) {
                TIMELINE_HILOGD(TIMELINE_MODULE_JS_NAPI, "Failed to set position state");
            } else {
                TIMELINE_HILOGD(TIMELINE_MODULE_JS_NAPI, "Succeed to set position state");
            }
        },
        [](napi_env env, napi_status status, void *data) {
            CompanyPositionAsyncCallbackInfo* asyncCallbackInfo = (CompanyPositionAsyncCallbackInfo *)data;
            napi_delete_async_work(env, asyncCallbackInfo->asyncWork);
            delete asyncCallbackInfo;
        },
        (void *)asyncCallbackInfo,
        &asyncCallbackInfo->asyncWork);

    NAPI_CALL(env, napi_queue_async_work(env, asyncCallbackInfo->asyncWork));

    TIMELINE_HILOGD(TIMELINE_MODULE_JS_NAPI, "return");
    return nullptr;
}

struct SleepTimeAsyncCallbackInfo {
    napi_env env;
    napi_async_work asyncWork;
    int32_t sleeptime;
    int32_t wakeuptime;
};

napi_value TimelineNapi::SetSleepTime(napi_env env, napi_callback_info info)
{
    TIMELINE_HILOGD(TIMELINE_MODULE_JS_NAPI, "enter");
    size_t argc = ARG_2;
    napi_value args[ARG_2] = {0};
    napi_value jsthis;
    void *data = nullptr;

    napi_status status = napi_get_cb_info(env, info, &argc, args, &jsthis, &data);
    NAPI_ASSERT(env, (status == napi_ok) && (argc >= 2), "Failed to get cb info");

    napi_valuetype type;
    NAPI_CALL(env, napi_typeof(env, args[0], &type));

    //NAPI_ASSERT(env, type == napi_number, "Wrong argument type. Numbers expected.");

    int32_t sleeptime = 0;
    NAPI_CALL(env, napi_get_value_int32(env, args[ARG_0], &sleeptime));

    NAPI_CALL(env, napi_typeof(env, args[1], &type));
    int32_t wakeuptime = 0;
    NAPI_CALL(env, napi_get_value_int32(env, args[ARG_1], &wakeuptime));

    SleepTimeAsyncCallbackInfo* asyncCallbackInfo = new SleepTimeAsyncCallbackInfo {
        .env = env,
        .asyncWork = nullptr,
    };

    asyncCallbackInfo->sleeptime = sleeptime;
    asyncCallbackInfo->wakeuptime = wakeuptime;

    napi_value resourceName;
    napi_create_string_latin1(env, "setSleepTime", NAPI_AUTO_LENGTH, &resourceName);

    napi_create_async_work(
        env,
        nullptr,
        resourceName,
        [](napi_env env, void *data) {
            SleepTimeAsyncCallbackInfo* asyncCallbackInfo = (SleepTimeAsyncCallbackInfo *)data;
            if (!TimelineClient::GetInstance().SetSleepTime(asyncCallbackInfo->sleeptime, asyncCallbackInfo->wakeuptime)) {
                TIMELINE_HILOGD(TIMELINE_MODULE_JS_NAPI, "Failed to set position state");
            } else {
                TIMELINE_HILOGD(TIMELINE_MODULE_JS_NAPI, "Succeed to set position state");
            }
        },
        [](napi_env env, napi_status status, void *data) {
            SleepTimeAsyncCallbackInfo* asyncCallbackInfo = (SleepTimeAsyncCallbackInfo *)data;
            napi_delete_async_work(env, asyncCallbackInfo->asyncWork);
            delete asyncCallbackInfo;
        },
        (void *)asyncCallbackInfo,
        &asyncCallbackInfo->asyncWork);

    NAPI_CALL(env, napi_queue_async_work(env, asyncCallbackInfo->asyncWork));

    TIMELINE_HILOGD(TIMELINE_MODULE_JS_NAPI, "return");
    return nullptr;
}

struct RestTimeAsyncCallbackInfo {
    napi_env env;
    napi_async_work asyncWork;
    int32_t resttimestart;
    int32_t resttimeend;
};

napi_value TimelineNapi::SetRestTime(napi_env env, napi_callback_info info)
{
    TIMELINE_HILOGD(TIMELINE_MODULE_JS_NAPI, "enter");
    size_t argc = ARG_2;
    napi_value args[ARG_2] = {0};
    napi_value jsthis;
    void *data = nullptr;

    napi_status status = napi_get_cb_info(env, info, &argc, args, &jsthis, &data);
    NAPI_ASSERT(env, (status == napi_ok) && (argc >= 2), "Failed to get cb info");

    napi_valuetype type;
    NAPI_CALL(env, napi_typeof(env, args[0], &type));
    NAPI_ASSERT(env, type == napi_number, "Wrong argument type. Numbers expected.");

    int32_t resttimestart = 0;
    NAPI_CALL(env, napi_get_value_int32(env, args[ARG_0], &resttimestart));

    NAPI_CALL(env, napi_typeof(env, args[1], &type));
    int32_t resttimeend = 0;
    NAPI_CALL(env, napi_get_value_int32(env, args[ARG_1], &resttimeend));

    RestTimeAsyncCallbackInfo* asyncCallbackInfo = new RestTimeAsyncCallbackInfo {
        .env = env,
        .asyncWork = nullptr,
    };

    asyncCallbackInfo->resttimestart = resttimestart;
    asyncCallbackInfo->resttimeend = resttimeend;

    napi_value resourceName;
    napi_create_string_latin1(env, "setRestTime", NAPI_AUTO_LENGTH, &resourceName);

    napi_create_async_work(
        env,
        nullptr,
        resourceName,
        [](napi_env env, void *data) {
            RestTimeAsyncCallbackInfo* asyncCallbackInfo = (RestTimeAsyncCallbackInfo *)data;
            if (!TimelineClient::GetInstance().SetRestTime(asyncCallbackInfo->resttimestart, asyncCallbackInfo->resttimeend)) {
                TIMELINE_HILOGD(TIMELINE_MODULE_JS_NAPI, "Failed to set position state");
            } else {
                TIMELINE_HILOGD(TIMELINE_MODULE_JS_NAPI, "Succeed to set position state");
            }
        },
        [](napi_env env, napi_status status, void *data) {
            RestTimeAsyncCallbackInfo* asyncCallbackInfo = (RestTimeAsyncCallbackInfo *)data;
            napi_delete_async_work(env, asyncCallbackInfo->asyncWork);
            delete asyncCallbackInfo;
        },
        (void *)asyncCallbackInfo,
        &asyncCallbackInfo->asyncWork);

    NAPI_CALL(env, napi_queue_async_work(env, asyncCallbackInfo->asyncWork));

    TIMELINE_HILOGD(TIMELINE_MODULE_JS_NAPI, "return");
    return nullptr;
}

struct WorkTimeAsyncCallbackInfo {
    napi_env env;
    napi_async_work asyncWork;
    int32_t worktimestart;
    int32_t worktimeend;
};

napi_value TimelineNapi::SetWorkTime(napi_env env, napi_callback_info info)
{
    TIMELINE_HILOGD(TIMELINE_MODULE_JS_NAPI, "enter");
    size_t argc = ARG_2;
    napi_value args[ARG_2] = {0};
    napi_value jsthis;
    void *data = nullptr;

    TIMELINE_HILOGD(TIMELINE_MODULE_JS_NAPI, "1");
    napi_status status = napi_get_cb_info(env, info, &argc, args, &jsthis, &data);
    NAPI_ASSERT(env, (status == napi_ok) && (argc >= 2), "Failed to get cb info");

TIMELINE_HILOGD(TIMELINE_MODULE_JS_NAPI, "2");
    napi_valuetype type;
    NAPI_CALL(env, napi_typeof(env, args[0], &type));

TIMELINE_HILOGD(TIMELINE_MODULE_JS_NAPI, "3");
    NAPI_ASSERT(env, type == napi_number, "Wrong argument type. Numbers expected.");
TIMELINE_HILOGD(TIMELINE_MODULE_JS_NAPI, "4");
    int32_t worktimestart = 0;
    NAPI_CALL(env, napi_get_value_int32(env, args[ARG_0], &worktimestart));
TIMELINE_HILOGD(TIMELINE_MODULE_JS_NAPI, "5");
    NAPI_CALL(env, napi_typeof(env, args[1], &type));
    int32_t worktimeend = 0;
    NAPI_CALL(env, napi_get_value_int32(env, args[ARG_1], &worktimeend));
TIMELINE_HILOGD(TIMELINE_MODULE_JS_NAPI, "6");
    WorkTimeAsyncCallbackInfo* asyncCallbackInfo = new WorkTimeAsyncCallbackInfo {
        .env = env,
        .asyncWork = nullptr,
    };
TIMELINE_HILOGD(TIMELINE_MODULE_JS_NAPI, "7");
    asyncCallbackInfo->worktimestart = worktimestart;
    asyncCallbackInfo->worktimeend = worktimeend;
TIMELINE_HILOGD(TIMELINE_MODULE_JS_NAPI, "9");
    napi_value resourceName;
    napi_create_string_latin1(env, "setWorkTime", NAPI_AUTO_LENGTH, &resourceName);
TIMELINE_HILOGD(TIMELINE_MODULE_JS_NAPI, "10");
    napi_create_async_work(
        env,
        nullptr,
        resourceName,
        [](napi_env env, void *data) {
            WorkTimeAsyncCallbackInfo* asyncCallbackInfo = (WorkTimeAsyncCallbackInfo *)data;
            if (!TimelineClient::GetInstance().SetWorkTime(asyncCallbackInfo->worktimestart, asyncCallbackInfo->worktimeend)) {
                TIMELINE_HILOGD(TIMELINE_MODULE_JS_NAPI, "Failed to set position state");
            } else {
                TIMELINE_HILOGD(TIMELINE_MODULE_JS_NAPI, "Succeed to set position state");
            }
        },
        [](napi_env env, napi_status status, void *data) {
            WorkTimeAsyncCallbackInfo* asyncCallbackInfo = (WorkTimeAsyncCallbackInfo *)data;
            napi_delete_async_work(env, asyncCallbackInfo->asyncWork);
            delete asyncCallbackInfo;
        },
        (void *)asyncCallbackInfo,
        &asyncCallbackInfo->asyncWork);
TIMELINE_HILOGD(TIMELINE_MODULE_JS_NAPI, "11");
    NAPI_CALL(env, napi_queue_async_work(env, asyncCallbackInfo->asyncWork));

    TIMELINE_HILOGD(TIMELINE_MODULE_JS_NAPI, "return");
    return nullptr;
}

struct FotcecasteAsyncCallbackInfo {
    napi_env env;
    napi_async_work asyncWork;
    int32_t time;
};

napi_value TimelineNapi::GetForcecast(napi_env env, napi_callback_info info)
{
    TIMELINE_HILOGD(TIMELINE_MODULE_JS_NAPI, "enter");
    size_t argc = ARG_1;
    napi_value args[ARG_1] = {0};
    napi_value jsthis;
    napi_value napiValue = nullptr;
    int32_t forcecastlValue;
    void *data = nullptr;

    napi_status status = napi_get_cb_info(env, info, &argc, args, &jsthis, &data);
    NAPI_ASSERT(env, (status == napi_ok) && (argc >= 1), "Failed to get cb info");

    napi_valuetype type;
    NAPI_CALL(env, napi_typeof(env, args[0], &type));

    NAPI_ASSERT(env, type == napi_number, "Wrong argument type. Numbers expected.");

    int32_t time = 0;
    NAPI_CALL(env, napi_get_value_int32(env, args[ARG_0], &time));

    napi_value resourceName;
    napi_create_string_latin1(env, "getForcecast", NAPI_AUTO_LENGTH, &resourceName);
    TimelineDatabaseInfo::ForcecastPosition g_forcecast = TimelineClient::GetInstance().GetForcecast(time);

    forcecastlValue = static_cast<int32_t>(g_forcecast);
    NAPI_CALL(env, napi_create_int32(env, forcecastlValue, &napiValue));
    TIMELINE_HILOGD(TIMELINE_MODULE_JS_NAPI, "GetForcecast:%{public}d", forcecastlValue);

    TIMELINE_HILOGD(TIMELINE_MODULE_JS_NAPI, "return");
    return napiValue;
}

EXTERN_C_START
/*
 * function for module exports
 */
static napi_value TimelineInit(napi_env env, napi_value exports)
{
    TIMELINE_HILOGD(TIMELINE_MODULE_JS_NAPI, "Enter");

    napi_value ret = TimelineNapi::Init(env, exports);

    TIMELINE_HILOGD(TIMELINE_MODULE_JS_NAPI, "Exit");

    return ret;
}
EXTERN_C_END

/*
 * Module definition
 */
static napi_module g_module = {
    .nm_version = 1,
    .nm_flags = 0,
    .nm_filename = "timeline",
    .nm_register_func = TimelineInit,
    .nm_modname = "timeline",
    .nm_priv = ((void *)0),
    .reserved = {0}
};

/*
 * Module registration
 */
extern "C" __attribute__((constructor)) void RegisterModule(void)
{
    napi_module_register(&g_module);
}
